package com.itheima.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

// 用于控制不同切面类的优先级。
// 若多个切面的切入点都匹配到了同一个目标方法，目标方法运行时，多个通知方法都会执行。
// 对于优先级高的，before通知会先执行，after通知会后执行。
//@Order(1)
@Slf4j
//@Aspect
//@Component
public class RecordTimeAspect {

    //execution语法规则：
    // execution(访问权限修饰符? 返回值类型 完整包名.类名.?方法名(参数列表) throws 异常?)
    //*代表任意符号(一般用于单个的 返回值、包名、类名、方法名、参数)
    //..代表任意多个参数(一般用于任意个数的 包的层级或方法参数)
    @Pointcut("execution(* com.itheima.service.impl.*.*(..))")
    private void pt(){};

    //环绕通知，若方法异常，方法后的通知不运行
    //环绕通知必须手动调用ProceedingJoinPoint，返回Object数据
    @Around("pt()")
    public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long begin = System.currentTimeMillis();

        Object result = proceedingJoinPoint.proceed();

        long end = System.currentTimeMillis();
        log.info("方法{}执行耗时：{} ms", proceedingJoinPoint.getSignature(),end - begin);
        return result;
    }

    //后置通知
    //可以手动调用JoinPoint返回切入点信息
    @After("pt()")
    public void after(JoinPoint joinPoint){
        Object target = joinPoint.getTarget();
        log.info("目标对象：{}",target);
        Object className = joinPoint.getTarget().getClass().getName();
        log.info("类名：{}",className);
        String methodName = joinPoint.getSignature().getName();
        log.info("方法名：{}",methodName);
        Object[] args = joinPoint.getArgs();
        log.info("方法参数：{}", Arrays.toString(args));

        log.info("方法执行后运行，无论异常");
    }

    //前置通知：
    @Before("pt()")
    public void before(){
        log.info("方法执行前运行");
    }

    //异常通知
    @AfterThrowing("pt()")
    public void afterThrowing(){
        log.info("方法执行异常后运行");
    }

    //返回后通知
    @AfterReturning("pt()")
    public void afterReturning(){
        log.info("方法正常返回后运行");
    }

}
